/*
 * -\-\-
 * --
 * Copyright (c) 2017-2018 Spotify AB
 * --
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 * -/-/-
 */

package com.example.ohos.architecture.blueprints.todoapp.tasks.domain;

import static com.google.common.base.Preconditions.checkNotNull;

import android.support.annotation.Nullable;

import com.example.ohos.architecture.blueprints.todoapp.data.Task;
import com.google.auto.value.AutoValue;
import com.google.common.base.Optional;
import com.google.common.collect.ImmutableList;

import java.util.ArrayList;
import java.util.List;

/**
 * @since 2021-07-12
 */
public final class TasksListModel {
    /**
     * TasksListModel DEFAULT
     */
    public static final TasksListModel DEFAULT = TasksListModel.builder().build();
    private ImmutableList<Task> tasks;
    private TasksFilterType filter;
    private boolean loading;

    private TasksListModel(
        @Nullable ImmutableList<Task> tasks,
        TasksFilterType filter,
        boolean loading) {
        this.tasks = tasks;
        this.filter = filter;
        this.loading = loading;
    }

    /**
     * tasks
     *
     * @return ImmutableList<Task>
     */
    @Nullable
    public ImmutableList<Task> tasks() {
        return tasks;
    }

    /**
     * filter
     *
     * @return TasksFilterType
     */
    public TasksFilterType filter() {
        return filter;
    }

    /**
     * loading
     *
     * @return boolean
     */
    public boolean loading() {
        return loading;
    }

    @Override
    public String toString() {
        return "TasksListModel{"
            + "tasks=" + tasks + ", "
            + "filter=" + filter + ", "
            + "loading=" + loading
            + "}";
    }

    /**
     * toBuilder
     *
     * @return Builder
     */
    public TasksListModel.Builder toBuilder() {
        return new Builder(this);
    }

    static final class Builder {
        private ImmutableList<Task> tasks;
        private TasksFilterType filter;
        private Boolean loading;

        Builder() {
        }

        private Builder(TasksListModel source) {
            this.tasks = source.tasks();
            this.filter = source.filter();
            this.loading = source.loading();
        }

        /**
         * tasks
         *
         * @param tasks tasks
         * @return Builder
         */
        public TasksListModel.Builder tasks(@Nullable ImmutableList<Task> tasks) {
            this.tasks = tasks;
            return this;
        }

        /**
         * filter
         *
         * @param filter filter
         * @return Builder
         */
        public TasksListModel.Builder filter(TasksFilterType filter) {
            if (filter == null) {
                throw new NullPointerException("Null filter");
            }
            this.filter = filter;
            return this;
        }

        /**
         * loading
         *
         * @param loading loading
         * @return Builder
         */
        public TasksListModel.Builder loading(boolean loading) {
            this.loading = loading;
            return this;
        }

        /**
         * build
         *
         * @return TasksListModel
         */
        public TasksListModel build() {
            String missing = "";
            if (this.filter == null) {
                missing += " filter";
            }
            if (this.loading == null) {
                missing += " loading";
            }
            if (!missing.isEmpty()) {
                throw new IllegalStateException("Missing required properties:" + missing);
            }
            return new TasksListModel(
                this.tasks,
                this.filter,
                this.loading);
        }
    }

    /**
     * builder
     *
     * @return Builder
     */
    public static Builder builder() {
        return new TasksListModel.Builder().loading(false).filter(TasksFilterType.ALL_TASKS);
    }

    /**
     * findTaskIndexById
     *
     * @param id id
     * @return int
     */
    public int findTaskIndexById(String id) {
        ImmutableList<Task> tasks = checkNotNull(tasks());
        int taskIndex = -1;
        for (int i = 0; i < tasks.size(); i++) {
            if (tasks.get(i).id().equals(id)) {
                taskIndex = i;
                break;
            }
        }
        return taskIndex;
    }

    /**
     * findTaskById
     *
     * @param id id
     * @return Optional<Task>
     */
    public Optional<Task> findTaskById(String id) {
        int taskIndex = findTaskIndexById(id);
        if (taskIndex < 0) {
            return Optional.absent();
        }
        return Optional.of((checkNotNull(tasks()).get(taskIndex)));
    }

    /**
     * withTasks
     *
     * @param tasks tasks
     * @return TasksListModel
     */
    public TasksListModel withTasks(ImmutableList<Task> tasks) {
        return toBuilder().tasks(tasks).build();
    }

    /**
     * withLoading
     *
     * @param loading loading
     * @return TasksListModel
     */
    public TasksListModel withLoading(boolean loading) {
        return toBuilder().loading(loading).build();
    }

    /**
     * withTasksFilter
     *
     * @param tasksFilter tasksFilter
     * @return TasksListModel
     */
    public TasksListModel withTasksFilter(TasksFilterType tasksFilter) {
        return toBuilder().filter(tasksFilter).build();
    }

    /**
     * withTaskAtIndex
     *
     * @param task task
     * @param index index
     * @return TasksListModel
     */
    public TasksListModel withTaskAtIndex(Task task, int index) {
        ImmutableList<Task> tasks = checkNotNull(tasks());
        assertIndexWithinBounds(index, tasks);

        ArrayList<Task> copy = new ArrayList<>(tasks);
        copy.set(index, task);
        return withTasks(ImmutableList.copyOf(copy));
    }

    static void assertIndexWithinBounds(int index, List<?> items) {
        if (index < 0 || index >= items.size()) {
            throw new IllegalArgumentException("Index out of bounds");
        }
    }

}
